/*-------------------------------------------------------------------------
 *
 * trigfuncs.c
 *	  有用的触发器支持的内置函数。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/backend/utils/adt/trigfuncs.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "commands/trigger.h"
#include "utils/builtins.h"
#include "utils/rel.h"


/*
 * suppress_redundant_updates_trigger
 *
 * 如果OLD和NEW记录相同，这个触发器函数将抑制更新的执行。
 */
Datum suppress_redundant_updates_trigger(PG_FUNCTION_ARGS)
{
	TriggerData *fc_trigdata = (TriggerData *) fcinfo->context;
	HeapTuple	fc_newtuple,
				fc_oldtuple,
				fc_rettuple;
	HeapTupleHeader fc_newheader,
				fc_oldheader;

	/* 确保作为触发器调用 */
	if (!CALLED_AS_TRIGGER(fcinfo))
		ereport(ERROR,
				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
				 errmsg("suppress_redundant_updates_trigger: must be called as trigger")));

	/* 并且在更新时被调用 */
	if (!TRIGGER_FIRED_BY_UPDATE(fc_trigdata->tg_event))
		ereport(ERROR,
				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
				 errmsg("suppress_redundant_updates_trigger: must be called on update")));

	/* 并且在更新前被调用 */
	if (!TRIGGER_FIRED_BEFORE(fc_trigdata->tg_event))
		ereport(ERROR,
				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
				 errmsg("suppress_redundant_updates_trigger: must be called before update")));

	/* 并且对每一行调用 */
	if (!TRIGGER_FIRED_FOR_ROW(fc_trigdata->tg_event))
		ereport(ERROR,
				(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
				 errmsg("suppress_redundant_updates_trigger: must be called for each row")));

	/* 获取元组数据，设置默认结果 */
	fc_rettuple = fc_newtuple = fc_trigdata->tg_newtuple;
	fc_oldtuple = fc_trigdata->tg_trigtuple;

	fc_newheader = fc_newtuple->t_data;
	fc_oldheader = fc_oldtuple->t_data;

	/* 如果元组负载相同... */
	if (fc_newtuple->t_len == fc_oldtuple->t_len &&
		fc_newheader->t_hoff == fc_oldheader->t_hoff &&
		(HeapTupleHeaderGetNatts(fc_newheader) ==
		 HeapTupleHeaderGetNatts(fc_oldheader)) &&
		((fc_newheader->t_infomask & ~HEAP_XACT_MASK) ==
		 (fc_oldheader->t_infomask & ~HEAP_XACT_MASK)) &&
		memcmp(((char *) fc_newheader) + SizeofHeapTupleHeader,
			   ((char *) fc_oldheader) + SizeofHeapTupleHeader,
			   fc_newtuple->t_len - SizeofHeapTupleHeader) == 0)
	{
		/* ... 则抑制更新 */
		fc_rettuple = NULL;
	}

	return PointerGetDatum(fc_rettuple);
}
